package com.shop.cereshop.commons.utils;

import org.locationtech.jts.geom.*;
import java.util.List;

/**
 * 综合地理工具类 (基于JTS)
 */
public class GeoUtils {

    private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
    private static final double EARTH_RADIUS = 6371393; // 平均半径

    /**
     * 计算两点间距离 - Haversine公式
     */
    public static double calculateDistance(double lng1, double lat1, double lng2, double lat2) {
        double radLat1 = Math.toRadians(lat1);
        double radLat2 = Math.toRadians(lat2);
        double a = radLat1 - radLat2;
        double b = Math.toRadians(lng1) - Math.toRadians(lng2);

        double distance = 2 * EARTH_RADIUS * Math.asin(Math.sqrt(
                Math.pow(Math.sin(a / 2), 2) +
                        Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)
        ));

        return Math.round(distance * 1000) / 1000.0; // 保留3位小数
    }

    /**
     * 判断点是否在圆形区域内
     */
    public static boolean isInCircle(double pointLng, double pointLat,
                                     double centerLng, double centerLat, double radius) {
        double distance = calculateDistance(pointLng, pointLat, centerLng, centerLat);
        return distance <= radius; 
    }

    /**
     * 判断点是否在多边形内
     */
    public static boolean isInPolygon(double pointLng, double pointLat, List<double[]> polygon) {
        Coordinate[] coordinates = new Coordinate[polygon.size() + 1];
        for (int i = 0; i < polygon.size(); i++) {
            double[] point = polygon.get(i);
            coordinates[i] = new Coordinate(point[0], point[1]);
        }
        coordinates[polygon.size()] = coordinates[0]; // 闭合多边形

        Polygon jtsPolygon = GEOMETRY_FACTORY.createPolygon(coordinates);
        Point testPoint = GEOMETRY_FACTORY.createPoint(new Coordinate(pointLng, pointLat));

        return jtsPolygon.contains(testPoint);
    }

    /**
     * 计算边界框
     */
    public static double[] calculateBoundingBox(double centerLng, double centerLat, double radius) {
        // 简化的边界框计算，精确计算需要考虑地球曲率
        double deltaLat = radius / EARTH_RADIUS * 180 / Math.PI;
        double deltaLng = deltaLat / Math.cos(Math.toRadians(centerLat));

        return new double[]{
                centerLng - deltaLng, // minLng
                centerLat - deltaLat, // minLat
                centerLng + deltaLng, // maxLng
                centerLat + deltaLat  // maxLat
        };
    }

    /**
     * 验证坐标有效性
     */
    public static boolean isValidCoordinate(double lng, double lat) {
        return lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90;
    }

    /**
     * 坐标点对象
     */
    public static class GeoPoint {
        private final double longitude;
        private final double latitude;

        public GeoPoint(double longitude, double latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }

        // getters
        public double getLongitude() { return longitude; }
        public double getLatitude() { return latitude; }
    }

    /**
     * 根据中心点和矩形长宽（米）计算四个顶点坐标（西北、东北、东南、西南）
     *
     * @param centerLng 中心经度
     * @param centerLat 中心纬度
     * @param widthMeters  矩形宽度（东西向，米）
     * @param heightMeters 矩形高度（南北向，米）
     * @return 顶点数组，顺序：西北→东北→东南→西南，每个元素为 {lng, lat}
     */
    public static double[][] rectangleVerticesByCenter(double centerLng, double centerLat,
                                                       double widthMeters, double heightMeters) {
        if (!isValidCoordinate(centerLng, centerLat)) {
            throw new IllegalArgumentException("invalid center coordinate");
        }
        if (widthMeters <= 0 || heightMeters <= 0) {
            throw new IllegalArgumentException("widthMeters and heightMeters must be positive");
        }
        // 使用球面近似：Δlat = d / R，Δlng = d / (R * cos(lat))
        double deltaLat = (heightMeters / 2.0) / EARTH_RADIUS * 180.0 / Math.PI;
        double deltaLng = (widthMeters / 2.0) / (EARTH_RADIUS * Math.cos(Math.toRadians(centerLat))) * 180.0 / Math.PI;

        double northLat = centerLat + deltaLat;
        double southLat = centerLat - deltaLat;
        double westLng = centerLng - deltaLng;
        double eastLng = centerLng + deltaLng;

        return new double[][]{
                {westLng, northLat},
                {eastLng, northLat},
                {eastLng, southLat},
                {westLng, southLat}
        };
    }
}